#include <graphics/VertexAttribute.h>
#include <graphics/GLBufferObjectManager.h>

namespace ne
{
    VertexAttribute::VertexAttribute(const uint32_t bufferSource,
                                     const AttributeSemantic semantic,
                                     const AttributeType type,
                                     const uint32_t count,
                                     const uint32_t stride,
                                     const size_t offset,
                                     const bool normalized)
        : mSource(bufferSource),
          mSemantic(semantic),
          mType(type),
          mCount(count),
          mStride(stride),
          mOffset(offset),
          mNormalized(normalized)
    {
    }

    uint32_t VertexAttribute::getSource() const
    {
        return mSource;
    }

    AttributeSemantic VertexAttribute::getSemantic() const
    {
        return mSemantic;
    }

    AttributeType VertexAttribute::getType() const
    {
        return mType;
    }

    uint32_t VertexAttribute::getCount() const
    {
        return mCount;
    }

    uint32_t VertexAttribute::getSize() const
    {
        return mCount * getAttributeSize();
    }

    GLDataType VertexAttribute::getGLDataType() const
    {
        return GetGLDataType(mType);
    }

    uint32_t VertexAttribute::getGLDataCount() const
    {
        return GetGLDataCount(mType);
    }

    uint32_t VertexAttribute::getAttributeSize() const
    {
        return GetAttributeSize(mType);
    }

    uint32_t VertexAttribute::getStride() const
    {
        return mStride;
    }

    bool VertexAttribute::isInterleaved() const
    {
        return (mStride != 0 && mStride != getAttributeSize());
    }

    size_t VertexAttribute::getOffset() const
    {
        return mOffset;
    }

    void* VertexAttribute::getOffsetPointer() const
    {
        return static_cast<void*>(static_cast<byte*>(0) + mOffset);
    }

    bool VertexAttribute::isNormalized() const
    {
        return mNormalized;
    }

    VertexAttribute::~VertexAttribute()
    {
    }

    PrimitiveType VertexAttribute::GetPrimitiveType(const std::string &str)
    {
        if (str == "POINTS") return PT_POINTS;
        else if (str == "LINES") return PT_LINES;
        else if (str == "LINE_STRIP") return PT_LINE_STRIP;
        else if (str == "LINE_LOOP") return PT_LINE_LOOP;
        else if (str == "TRIANGLES") return PT_TRIANGLES;
        else if (str == "TRIANGLE_STRIP") return PT_TRIANGLE_STRIP;
        else if (str == "TRIANGLE_FAN") return PT_TRIANGLE_FAN;
        else if (str == "QUADS") return PT_QUADS;
        else if (str == "QUAD_STRIP") return PT_QUAD_STRIP;
        else if (str == "POLYGON") return PT_POLYGON;
        return PT_TRIANGLES;
    }

    GLDataType VertexAttribute::GetGLDataType(const AttributeType type)
    {
        switch (type)
        {
            case VAT_CHAR1:
            case VAT_CHAR2:
            case VAT_CHAR3:
            case VAT_CHAR4:
                return GLDT_CHAR;

            case VAT_BYTE1:
            case VAT_BYTE2:
            case VAT_BYTE3:
            case VAT_BYTE4:
                return GLDT_BYTE;

            case VAT_SHORT1:
            case VAT_SHORT2:
            case VAT_SHORT3:
            case VAT_SHORT4:
                return GLDT_SHORT;

            case VAT_UNSIGNED_SHORT1:
            case VAT_UNSIGNED_SHORT2:
            case VAT_UNSIGNED_SHORT3:
            case VAT_UNSIGNED_SHORT4:
                return GLDT_UNSIGNED_SHORT;

            case VAT_INT1:
            case VAT_INT2:
            case VAT_INT3:
            case VAT_INT4:
                return GLDT_INT;

            case VAT_UNSIGNED_INT1:
            case VAT_UNSIGNED_INT2:
            case VAT_UNSIGNED_INT3:
            case VAT_UNSIGNED_INT4:
                return GLDT_UNSIGNED_INT;

            case VAT_FLOAT1:
            case VAT_FLOAT2:
            case VAT_FLOAT3:
            case VAT_FLOAT4:
                return GLDT_FLOAT;

            case VAT_DOUBLE1:
            case VAT_DOUBLE2:
            case VAT_DOUBLE3:
            case VAT_DOUBLE4:
                return GLDT_DOUBLE;

            default:
                return GLDT_BYTE;
        }
    }

    uint32_t VertexAttribute::GetGLDataCount(const AttributeType type)
    {
        switch (type)
        {
            case VAT_CHAR1:
            case VAT_BYTE1:
            case VAT_SHORT1:
            case VAT_UNSIGNED_SHORT1:
            case VAT_INT1:
            case VAT_UNSIGNED_INT1:
            case VAT_FLOAT1:
            case VAT_DOUBLE1:
                return 1;

            case VAT_CHAR2:
            case VAT_BYTE2:
            case VAT_SHORT2:
            case VAT_UNSIGNED_SHORT2:
            case VAT_INT2:
            case VAT_UNSIGNED_INT2:
            case VAT_FLOAT2:
            case VAT_DOUBLE2:
                return 2;

            case VAT_CHAR3:
            case VAT_BYTE3:
            case VAT_SHORT3:
            case VAT_UNSIGNED_SHORT3:
            case VAT_INT3:
            case VAT_UNSIGNED_INT3:
            case VAT_FLOAT3:
            case VAT_DOUBLE3:
                return 3;

            case VAT_CHAR4:
            case VAT_BYTE4:
            case VAT_SHORT4:
            case VAT_UNSIGNED_SHORT4:
            case VAT_INT4:
            case VAT_UNSIGNED_INT4:
            case VAT_FLOAT4:
            case VAT_DOUBLE4:
                return 4;

            default:
                return 0;
        }
    }

    uint32_t VertexAttribute::GetAttributeSize(const AttributeType type)
    {
        switch (type)
        {
            case VAT_CHAR1:
            case VAT_BYTE1:
                return 1;

            case VAT_CHAR2:
            case VAT_BYTE2:
            case VAT_SHORT1:
            case VAT_UNSIGNED_SHORT1:
                return 2;

            case VAT_CHAR3:
            case VAT_BYTE3:
                return 3;

            case VAT_CHAR4:
            case VAT_BYTE4:
            case VAT_SHORT2:
            case VAT_UNSIGNED_SHORT2:
            case VAT_INT1:
            case VAT_UNSIGNED_INT1:
            case VAT_FLOAT1:
                return 4;

            case VAT_SHORT3:
            case VAT_UNSIGNED_SHORT3:
                return 6;

            case VAT_SHORT4:
            case VAT_UNSIGNED_SHORT4:
            case VAT_INT2:
            case VAT_UNSIGNED_INT2:
            case VAT_FLOAT2:
            case VAT_DOUBLE1:
                return 8;

            case VAT_INT3:
            case VAT_UNSIGNED_INT3:
            case VAT_FLOAT3:
                return 12;

            case VAT_INT4:
            case VAT_UNSIGNED_INT4:
            case VAT_FLOAT4:
            case VAT_DOUBLE2:
                return 16;

            case VAT_DOUBLE3:
                return 24;

            case VAT_DOUBLE4:
                return 32;

            default:
                return 0;
        }
    }

    AttributeSemantic VertexAttribute::GetAttributeSemantic(const std::string &str)
    {
        if (str == "POSITION") return VAS_POSITION;
        else if (str == "NORMAL") return VAS_NORMAL;
        else if (str == "COLOR") return VAS_COLOR;
        else if (str == "SECONDARY_COLOR") return VAS_SECONDARY_COLOR;
        else if (str == "FOG_COORD") return VAS_FOG_COORD;
        else if (str == "EDGE_FLAG") return VAS_EDGE_FLAG;
        else if (str == "TEXTURE_COORD") return VAS_TEXTURE_COORD;
        else if (str == "TEXTURE_COORD0") return VAS_TEXTURE_COORD0;
        else if (str == "TEXTURE_COORD1") return VAS_TEXTURE_COORD1;
        else if (str == "TEXTURE_COORD2") return VAS_TEXTURE_COORD2;
        else if (str == "TEXTURE_COORD3") return VAS_TEXTURE_COORD3;
        else if (str == "TEXTURE_COORD4") return VAS_TEXTURE_COORD3;
        else if (str == "TEXTURE_COORD5") return VAS_TEXTURE_COORD3;
        else if (str == "TEXTURE_COORD6") return VAS_TEXTURE_COORD3;
        else if (str == "TEXTURE_COORD7") return VAS_TEXTURE_COORD3;
        else if (str == "BLEND_WEIGHT") return VAS_BLEND_WEIGHT;
        else if (str == "BLEND_INDEX") return VAS_BLEND_INDEX;
        else if (str == "TANGENT") return VAS_TANGENT;
        else if (str == "BINORMAL") return VAS_BINORMAL;
        else if (str == "ATTRIBUTE") return VAS_ATTRIBUTE;
        else if (str == "ATTRIBUTE0") return VAS_ATTRIBUTE0;
        else if (str == "ATTRIBUTE1") return VAS_ATTRIBUTE1;
        else if (str == "ATTRIBUTE2") return VAS_ATTRIBUTE2;
        else if (str == "ATTRIBUTE3") return VAS_ATTRIBUTE3;
        else if (str == "ATTRIBUTE4") return VAS_ATTRIBUTE4;
        else if (str == "ATTRIBUTE5") return VAS_ATTRIBUTE5;
        return VAS_ATTRIBUTE;
    }

    AttributeType VertexAttribute::GetAttributeType(const std::string &str)
    {
        if (str == "CHAR") return VAT_CHAR;
        else if (str == "CHAR1") return VAT_CHAR1;
        else if (str == "CHAR2") return VAT_CHAR2;
        else if (str == "CHAR3") return VAT_CHAR3;
        else if (str == "CHAR4") return VAT_CHAR4;
        else if (str == "BYTE") return VAT_BYTE;
        else if (str == "BYTE1") return VAT_BYTE1;
        else if (str == "BYTE2") return VAT_BYTE2;
        else if (str == "BYTE3") return VAT_BYTE3;
        else if (str == "BYTE4") return VAT_BYTE4;
        else if (str == "SHORT") return VAT_SHORT;
        else if (str == "SHORT1") return VAT_SHORT1;
        else if (str == "SHORT2") return VAT_SHORT2;
        else if (str == "SHORT3") return VAT_SHORT3;
        else if (str == "SHORT4") return VAT_SHORT4;
        else if (str == "UNSIGNED_SHORT") return VAT_UNSIGNED_SHORT;
        else if (str == "UNSIGNED_SHORT1") return VAT_UNSIGNED_SHORT1;
        else if (str == "UNSIGNED_SHORT2") return VAT_UNSIGNED_SHORT2;
        else if (str == "UNSIGNED_SHORT3") return VAT_UNSIGNED_SHORT3;
        else if (str == "UNSIGNED_SHORT4") return VAT_UNSIGNED_SHORT4;
        else if (str == "INT") return VAT_INT;
        else if (str == "INT1") return VAT_INT1;
        else if (str == "INT2") return VAT_INT2;
        else if (str == "INT3") return VAT_INT3;
        else if (str == "INT4") return VAT_INT4;
        else if (str == "UNSIGNED_INT") return VAT_UNSIGNED_INT;
        else if (str == "UNSIGNED_INT1") return VAT_UNSIGNED_INT1;
        else if (str == "UNSIGNED_INT2") return VAT_UNSIGNED_INT2;
        else if (str == "UNSIGNED_INT3") return VAT_UNSIGNED_INT3;
        else if (str == "UNSIGNED_INT4") return VAT_UNSIGNED_INT4;
        else if (str == "FLOAT") return VAT_FLOAT;
        else if (str == "FLOAT1") return VAT_FLOAT1;
        else if (str == "FLOAT2") return VAT_FLOAT2;
        else if (str == "FLOAT3") return VAT_FLOAT3;
        else if (str == "FLOAT4") return VAT_FLOAT4;
        else if (str == "DOUBLE") return VAT_DOUBLE;
        else if (str == "DOUBLE1") return VAT_DOUBLE1;
        else if (str == "DOUBLE2") return VAT_DOUBLE2;
        else if (str == "DOUBLE3") return VAT_DOUBLE3;
        else if (str == "DOUBLE4") return VAT_DOUBLE4;
        else if (str == "RGB") return VAT_RGB;
        else if (str == "RGBA") return VAT_RGBA;
        else if (str == "COLOR_RGB") return VAT_COLOR_RGB;
        else if (str == "COLOR_RGBA") return VAT_COLOR_RGBA;
        return VAT_FLOAT;
    }

    AttributeType VertexAttribute::GetFloatAttributeType(const uint32_t size)
    {
        switch (size)
        {
            case 4: return VAT_FLOAT1;
            case 8: return VAT_FLOAT2;
            case 12: return VAT_FLOAT3;
            case 16: return VAT_FLOAT4;
            default: return VAT_FLOAT;
        }
    }

    AttributeType VertexAttribute::GetDoubleAttributeType(const uint32_t size)
    {
        switch (size)
        {
            case 8: return VAT_DOUBLE1;
            case 16: return VAT_DOUBLE2;
            case 24: return VAT_DOUBLE3;
            case 32: return VAT_DOUBLE4;
            default: return VAT_DOUBLE;
        }
    }

    AttributeType VertexAttribute::GetColorAttributeType(const uint32_t size)
    {
        switch (size)
        {
            case 3: return VAT_RGB;
            case 4: return VAT_RGBA;
            case 12: return VAT_COLOR_RGB;
            case 16: return VAT_COLOR_RGBA;
            default: return VAT_COLOR_RGBA;
        }
    }

    AttributeType VertexAttribute::GetIntAttributeType(const uint32_t size, const uint32_t count)
    {
        switch (count)
        {
            case 1:
            {
                switch (size)
                {
                    case 1: return VAT_BYTE1;
                    case 2: return VAT_UNSIGNED_SHORT1;
                    case 4: return VAT_UNSIGNED_INT1;
                    default: return VAT_BYTE1;
                }
            }
            case 2:
            {
                switch (size)
                {
                    case 2: return VAT_BYTE2;
                    case 4: return VAT_UNSIGNED_SHORT2;
                    case 8: return VAT_UNSIGNED_INT2;
                    default: return VAT_BYTE2;
                }
            }
            case 3:
            {
                switch (size)
                {
                    case 3: return VAT_BYTE3;
                    case 6: return VAT_UNSIGNED_SHORT3;
                    case 12: return VAT_UNSIGNED_INT3;
                    default: return VAT_BYTE3;
                }
            }
            case 4:
            {
                switch (size)
                {
                    case 4: return VAT_BYTE4;
                    case 8: return VAT_UNSIGNED_SHORT4;
                    case 16: return VAT_UNSIGNED_INT4;
                    default: return VAT_BYTE4;
                }
            }
            default: return VAT_BYTE;
        }
    }

    AttributeType VertexAttribute::GetIndexAttributeType(const uint32_t count)
    {
        if (count <= UCHAR_MAX) return VAT_BYTE;
        else if (count <= USHRT_MAX) return VAT_UNSIGNED_SHORT;
        return VAT_UNSIGNED_INT;
    }
}
